#include <string.h>
#include <cmath>
#include <iostream>  //预处理器指令 a PREPROCESSOR directive
using namespace std; // 使定义可见   make definitions visible
#include <vector>
#include <array>
#include <new>

/*
  13.3 银行客户案例

  虚方法(virtual method)

*/

// formatting stuff
// 使用格式化方法setf()和precision()将浮点值的输出模式设置为定点，即包含两位小数
typedef std::ios_base::fmtflags format;
typedef std::streamsize precis;
format setFormat();
void restore(format f, precis p);

// 银行客户类
// Brass Account Class
class Brass
{
private:
    std::string fullName; //姓名
    long acctNum; //账号
    double balance; //余额
public:
    Brass(const std::string & s = "Nullbody", long an = -1, double bal = 0.0);

    void Deposit(double amt); //创建账户
    virtual void Withdraw(double amt); //取款
    double Balance() const; //得到余额
    virtual void ViewAcct() const; //显示账户信息
    virtual ~Brass() {}
};

//Brass Plus Account Class
class BrassPlus : public Brass
{
private:
    double maxLoan; //透支上限
    double rate; //透支贷款利率
    double owesBank; //当前的透支总额
public:
    //BrassPlus账户限制了客户的透支款额。默认为500元,贷款收取利息。默认为11.125%
    BrassPlus(const std::string & s = "Nullbody", long an = -1,
            double bal = 0.0, double ml = 500,
            double r = 0.11125);
    
    BrassPlus(const Brass & ba, double ml = 500, double r = 0.11125);

    //●Brass类和BrassPlus类都声明了ViewAcct()和Withdraw()方法，但BrassPlus对象和Brass对象的这些方法的行为是不同的；
    virtual void ViewAcct()const;      //显示操作必须显示BrassPlus账户的其他信息
    virtual void Withdraw(double amt); //对于取款操作，必须考虑透支保护

    //新关键字virtual。这些方法被称为虚方法(virtual method);程序将根据引用或指针指向的对象的类型来选择方法。

    void ResetMax(double m) { maxLoan = m; }
    void ResetRate(double r) { rate = r; };
    void ResetOwes() { owesBank = 0; }
};

int main()
{
  Brass dom("Dominic Banker", 10001, 2000);
  BrassPlus dot("Dorothy Banker", 10002, 1000);

  Brass &b1ref = dom;
  Brass &b2ref = dot;

  //如果方法是通过引用或指针而不是对象调用的，它将确定使用哪一种方法。
  //如果没有使用关键字virtual,程序将根据引用类型或指针类型选择方法；如果使用了virtual,程序将根据引用或指针指向的对象的类型来选择方法。
  b1ref.ViewAcct(); // use Brass::ViewAcct()
  b2ref.ViewAcct(); // use BrassPlus::ViewAcct()

  b2ref.Withdraw(1400);
  b2ref.ViewAcct(); // use BrassPlus::ViewAcct()


  const int CLIENTS = 4;
  Brass *p_clients[CLIENTS];
  string temp;
  long tempnum;
  double tempbal;

  for (int i = 0; i < CLIENTS; i++)
  {
    p_clients[i] = new Brass(temp, tempnum, tempbal);
  }

  for (int i = 0; i < CLIENTS; i++)
  {
    delete p_clients[i]; // free memory
  }


}



// Brass methods
Brass::Brass(const string & s, long an, double bal)
{
    fullName = s;
    acctNum = an;
    balance = bal;
}

void Brass::Deposit(double amt)
{
    if (amt < 0)
        cout << "Negative deposit not allowed; "
             << "deposit is cancelled.\n";
    else
        balance += amt;
}

void Brass::Withdraw(double amt)
{
    // set up ###.## format
    format initialState = setFormat();
    precis prec = cout.precision(2);

    if (amt < 0)
        cout << "Withdrawal amount must be positive; " << "withdrawal canceled.\n";
    else if (amt <= balance)
        balance -= amt;
    else
        //20000.00美元的提款金额超过了您的余额。
        //提款已取消。
        cout << "Withdrawal amount of $" << amt << " exceeds your balance.\n"
             << "Withdrawal canceled.\n";
    restore(initialState, prec);
}
double Brass::Balance() const
{
    return balance;
}

void Brass::ViewAcct() const
{   
    cout << endl;
     // set up ###.## format
    format initialState = setFormat();
    precis prec = cout.precision(2);
    cout << "Client: " << fullName << endl;
    cout << "Account Number: " << acctNum << endl;
    cout << "Balance: $" << balance << endl;
    restore(initialState, prec); // Restore original format
}

// BrassPlus Methods
BrassPlus::BrassPlus(const string & s, long an, double bal,
           double ml, double r) : Brass(s, an, bal)
{
    maxLoan = ml;
    owesBank = 0.0;
    rate = r;
}

BrassPlus::BrassPlus(const Brass & ba, double ml, double r)
           : Brass(ba)   // uses implicit copy constructor
{
    maxLoan = ml;
    owesBank = 0.0;
    rate = r;
}

// redefine how ViewAcct() works
void BrassPlus::ViewAcct() const
{
    // set up ###.## format
    format initialState = setFormat();
    precis prec = cout.precision(2);

    Brass::ViewAcct();   // display base portion
    cout << "Maximum loan: $" << maxLoan << endl;
    cout << "Owed to bank: $" << owesBank << endl;
    cout.precision(3);  // ###.### format
    cout << "Loan Rate: " << 100 * rate << "%\n";
    restore(initialState, prec); 
}

// redefine how Withdraw() works
void BrassPlus::Withdraw(double amt)
{   
    cout  << endl << "进行取款 ----- " << endl;
    // set up ###.## format
    format initialState = setFormat();
    precis prec = cout.precision(2);

    double bal = Balance();
    if (amt <= bal)
        Brass::Withdraw(amt);
    else if ( amt <= bal + maxLoan - owesBank)
    {
        double advance = amt - bal;
        owesBank += advance * (1.0 + rate);
        cout << "Bank advance: $" << advance << endl; //预付款 
        cout << "Finance charge: $" << advance * rate << endl; //财务费用
        Deposit(advance);
        Brass::Withdraw(amt);
    }
    else
        //超出信用额度。交易已取消。
        cout << "Credit limit exceeded. Transaction cancelled.\n";
    restore(initialState, prec); 
}

format setFormat()
{
    // set up ###.## format
    return cout.setf(std::ios_base::fixed, 
                std::ios_base::floatfield);
} 

void restore(format f, precis p)
{
    cout.setf(f, std::ios_base::floatfield);
    cout.precision(p);
}
